///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_GEOMETRY_CSLOPE_H_
#define _BUOLA_GEOMETRY_CSLOPE_H_

#include <buola/utility/cpoint.h>

namespace buola {

///\addtogroup geometry
///@{

/////////////////////////////////////////////////////////////////////////////

/// 2D skioe

/// the main use of this class is to specify the size of a window or a
/// rectangle
///
/////////////////////////////////////////////////////////////////////////////

template<typename tType>
class CSlope
{
    typedef std::numeric_limits<tType> TLimits;
    static constexpr tType cUndefined=TLimits::has_infinity?TLimits::infinity():TLimits::max();

public:
    typedef tType TType;
    typedef typename std::conditional<TLimits::is_integer,double,tType>::type TReal;

///\name constructor and destructor
//@{
    ///default constructor (leaves uninitialized)
    CSlope()=default;

    ///copy constructor
    CSlope(const CSlope&)=default;

    ///from size of other type
    template<typename tO,OEnableIf<detail::QAutomaticPointConversion<tO,tType>> = 0>
    CSlope(const CSlope<tO> &pO)
        :   x(pO.x)
        ,   y(pO.y)
    {}

    template<typename tO,OEnableIfNot<detail::QAutomaticPointConversion<tO,tType>> = 0>
    explicit CSlope(const CSlope<tO> &pO)
        :   x(pO.x)
        ,   y(pO.y)
    {}

    ///from x and y
    CSlope(tType px,tType py)
        :   x(px)
        ,   y(py)
    {}

    ///undefined constructor
    CSlope(NUndefined)
        :   x(cUndefined)
    {}

    ///from two points
    CSlope(const CPoint<tType> &p1,const CPoint<tType> &p2)
        :   x(p2.x-p1.x)
        ,   y(p2.y-p1.y)
    {}
//@}

    ///equality operator
    bool operator==(const CSlope &pRH) const
    {
        return (x*pRH.y==y*pRH.x)&&x*pRH.x>0;
    }

    ///inequality operator
    bool operator!=(const CSlope &pRH) const
    {
        return !operator==(pRH);
    }

    ///less-than comparison operator
    bool operator<(const CSlope &pRH) const
    {
        return y*pRH.x<x*pRH.y;
    }
    
    ///returns true if the size is undefined
    bool Undefined() const
    {
        return (x==cUndefined);
    }

    tType Normalize()
    {
        double lMag;

        if(x==0.0)
        {
            if(y>0.0)
            {
                lMag=y;
                y=1.0;
            }
            else
            {
                lMag=-y;
                y=-1.0;
            }
        }
        else if(y==0.0)
        {
            if(x>0.0)
            {
                lMag=x;
                x=1.0;
            }
            else
            {
                lMag=-x;
                x=-1.0;
            }
        }
        else
        {
            lMag=hypot(x,y);
            x/=lMag;
            y/=lMag;
        }

        return lMag;
    }

    tType x;   ///< x slope
    tType y;   ///< y slope
};

///\relates CSlope
///\brief negates the size
template<typename tType>
inline CSlope<tType> operator-(const CSlope<tType> &pSlope)
{
    return CSlope<tType>(-pSlope.x,-pSlope.y);
}

///\relates CSlope
///\brief output operator
template<typename tType>
inline io::CTextWriter &operator<<(io::CTextWriter &pWriter,const CSlope<tType> &pSlope)
{
    pWriter << '(' << pSlope.x << '/' << pSlope.y <<')';
    return pWriter;
}

typedef CSlope<double> CSlope_d;
typedef CSlope<float> CSlope_f;
typedef CSlope<int> CSlope_i;

///@}

/*namespace buola*/ }

#endif
